package com.wmx.www.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.wmx.www.config.MyWebMvcConfigurer;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.MultipartConfigElement;
import javax.servlet.http.HttpServletRequest;
import javax.swing.filechooser.FileSystemView;
import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * Spring Boot 文件上传、url 映射虚拟路径
 * Html5 + From 多、单文件上传
 *
 * @author wangMaoXiong
 * Created by Administrator on 2019/3/17 0017.
 */
@Controller
@SuppressWarnings("Duplicates")
public class UploadFileController {

    private static final Logger logger = LoggerFactory.getLogger(UploadFileController.class);

    /**
     * 请求 url 中的资源映射，不推荐写死在代码中，最好提供可配置，如 /uploadFiles/**
     */
    @Value("${uploadFile.resourceHandler}")
    private String resourceHandler;

    /**
     * 上传文件保存的本地目录，使用 @Value获 取全局配置文件中配置的属性值，如 E:/wmx/uploadFiles/
     */
    @Value("${uploadFile.location}")
    private String uploadFileLocation;

    @Resource
    private MultipartConfigElement multipartConfigElement;

    /**
     * http://localhost:8080/fileServer/uploadFile
     * 文件上传，因为只是演示，所以使用 @ResponseBody 将结果直接返回给页面
     *
     * @param singleFile ：上传的文件对象, 名称必须和页面<input type="file" name="xxx"/>的name属性值一致
     * @param request    ：请求对象
     * @return ：返回的是文件名称，将来客户端可以用来下载
     * @throws IOException
     */
    @PostMapping("uploadFile")
    @ResponseBody
    public String uploadFile(MultipartFile singleFile, HttpServletRequest request, String userName, String password) throws IOException {
        if (singleFile == null || singleFile.isEmpty()) {
            logger.debug("上传文件为空...");
            return "上传文件为空...";
        }
        System.out.println("userName=" + userName + "，password=" + password);

        //basePath拼接完成后，形如：http://192.168.1.20:8080/fileServer
        String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
        String fileName = singleFile.getOriginalFilename();
        String fileServerPath = basePath + resourceHandler.substring(0, resourceHandler.lastIndexOf("/") + 1) + fileName;
        logger.debug("文件访问路径：{}", fileServerPath);

        File saveFile = new File(uploadFileLocation, fileName);
        /**
         * 文件保存，注意目的文件夹必须事先存在，否则保存时报错
         * 在{@link MyWebMvcConfigurer}中已经处理了，如果不存在，自动新建存储目录
         */
        singleFile.transferTo(saveFile);
        logger.info("文件保存路径：{}", saveFile.getPath());
        return "<a target='_blank' href='" + fileServerPath + "'>" + fileServerPath + "</a>";
    }

    /**
     * http://localhost:8080/fileServer/singleFileUpload
     * 单文件上传
     *
     * @param uploadFile：SpringMVC文件上传封装对象，多文件上传时，使用MultipartFile列表(List)或者数组(Array)都可以
     */
    @RequestMapping(value = "singleFileUpload", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> singleFileUpload(@RequestParam MultipartFile uploadFile, String userName, String password) throws IOException {
        System.out.println("userName=" + userName + "，password=" + password);

        // String getOriginalFilename() : 包含文件名与格式，如:123.mp4
        // long getSize() ：返回文件的大小（字节）。如果为空，则为0
        // String getContentType() ：返回内容类型，如果未定义，则返回{@code null}（或在多部分表单中未选择任何文件）
        // String byteCountToDisplaySize(final long size)：返回文件大小的可读版本,如 20.77M
        // byte[] bytes = uploadFile.getBytes(); // // 获取文件字节内容
        String fileNameAndFormat = uploadFile.getOriginalFilename();
        Long fileSizeLong = uploadFile.getSize();
        String contentType = uploadFile.getContentType();
        String fileSizeStr = FileUtils.byteCountToDisplaySize(fileSizeLong);

        File homeDirectory = FileSystemView.getFileSystemView().getHomeDirectory();
        File file = new File(homeDirectory, fileNameAndFormat);
        uploadFile.transferTo(file);

        Map<String, Object> map = new HashMap<>(8);
        map.put("fileNameAndFormat", fileNameAndFormat);
        map.put("fileSizeLong", fileSizeLong);
        map.put("fileSizeStr", fileSizeStr);
        map.put("contentType", contentType);
        map.put("transferTo", file.getAbsolutePath());

        // {
        //     "fileNameAndFormat":"Firefox-latest.exe",
        //     "fileSizeLong":394192,
        //     "transferTo":"E:\\桌面\\Firefox-latest.exe",
        //     "contentType":"application/x-msdownload"
        // }
        return map;
    }

    /**
     * http://localhost:8080/fileServer/multipleFileUpload
     * 多文件上传
     *
     * @param multipleFile：SpringMVC文件上传封装对象，多文件上传时，使用MultipartFile列表(List)或者数组(Array)都可以
     */
    @RequestMapping(value = "multipleFileUpload", method = RequestMethod.POST)
    @ResponseBody
    public List<Map<String, Object>> multipleFileUpload(@RequestParam List<MultipartFile> multipleFile, String userName, String password) throws IOException {
        System.out.println("userName=" + userName + "，password=" + password);

        List<Map<String, Object>> mapList = new ArrayList<>();
        for (MultipartFile uploadFile : multipleFile) {
            // String getOriginalFilename() : 包含文件名与格式，如:123.mp4
            // long getSize() ：返回文件的大小（字节）。如果为空，则为0
            // String getContentType() ：返回内容类型，如果未定义，则返回{@code null}（或在多部分表单中未选择任何文件）
            String fileNameAndFormat = uploadFile.getOriginalFilename();
            Long fileSizeLong = uploadFile.getSize();
            String contentType = uploadFile.getContentType();

            File homeDirectory = FileSystemView.getFileSystemView().getHomeDirectory();
            File file = new File(homeDirectory, fileNameAndFormat);
            uploadFile.transferTo(file);

            Map<String, Object> map = new HashMap<>(8);
            map.put("fileNameAndFormat", fileNameAndFormat);
            map.put("fileSizeLong", fileSizeLong);
            map.put("contentType", contentType);
            map.put("transferTo", file.getAbsolutePath());
            mapList.add(map);
        }
        // {
        //     "fileNameAndFormat":"Firefox-latest.exe",
        //     "fileSizeLong":394192,
        //     "transferTo":"E:\\桌面\\Firefox-latest.exe",
        //     "contentType":"application/x-msdownload"
        // }
        return mapList;
    }

    /**
     * http://localhost:8080/fileServer/getMultipartConfigElement
     * <pre>
     * 获取文件上传配置信息：
     *  spring.servlet.multipart.file-size-threshold=0B #将文件写入磁盘的阈值.
     *  spring.servlet.multipart.location= # 上传文件的临时存储目录(默认为系统临时目录).
     *  spring.servlet.multipart.max-file-size=1MB # 单个文件支持的最大大小.
     *  spring.servlet.multipart.max-request-size=10MB # 单次请求时，全部文件的不能超过的总大小.
     * </pre>
     *
     * @return
     */
    @GetMapping("getMultipartConfigElement")
    @ResponseBody
    public Map<String, Object> getMultipartConfigElement() {
        int fileSizeThreshold = multipartConfigElement.getFileSizeThreshold();
        String location = multipartConfigElement.getLocation();
        long maxFileSize = multipartConfigElement.getMaxFileSize();
        long maxRequestSize = multipartConfigElement.getMaxRequestSize();

        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("fileSizeThreshold", fileSizeThreshold);
        dataMap.put("location", location);
        dataMap.put("maxFileSize", maxFileSize);
        dataMap.put("maxRequestSize", maxRequestSize);

        // application.yml 中配置时：
        // {
        //   "maxRequestSize": 1073741824,
        //   "fileSizeThreshold": 0,
        //   "location": "E:/wmx/uploadTemps/",
        //   "maxFileSize": 1048576000
        // }

        // com.wmx.www.config.MyWebMvcConfigurer.multipartConfigElement 中配置时：
        // {
        //   "maxRequestSize": 1073741824,
        //   "fileSizeThreshold": 0,
        //   "location": "C:\\Users\\Think\\AppData\\Local\\Temp\\file-server",
        //   "maxFileSize": 1048576000
        // }

        // 没有配置，默认时：
        // {
        //   "maxRequestSize": 10485760,
        //   "fileSizeThreshold": 0,
        //   "location": "",
        //   "maxFileSize": 1048576
        // }

        return dataMap;
    }

    /**
     * 解决文件上传时临时目录找不到的问题，万一找不到，直接给它创建。
     * java.io.IOException: The temporary upload location [/tmp/tomcat.2877894020006618597.7030/work/Tomcat/localhost/ROOT] is not valid.
     * <p>
     * <pre>
     * http://localhost:8080/fileServer/createDirByPath?dirPath=E:/temp/a
     *  {
     *   "absolutePath": "E:\\temp\\a",
     *   "lastModifiedTime": "2023-10-28 10:03:29"
     * }
     * </pre>
     * <pre>
     * http://localhost:8080/fileServer/createDirByPath?dirPath=C:/Users/Think/AppData/Local/Temp/tomcat.8937263102917318832.8080/work/Tomcat/localhost/ROOT
     *  {
     *   "absolutePath": "C:\\Users\\Think\\AppData\\Local\\Temp\\tomcat.8937263102917318832.8080\\work\\Tomcat\\localhost\\ROOT",
     *   "lastModifiedTime": "2023-10-28 10:05:33"
     * }
     * </pre>
     * <pre>
     * http://localhost:8080/fileServer/createDirByPath?dirPath=/tmp/tomcat.2877894020006618597.7030/work/Tomcat/localhost/ROOT
     * windows系统上的效果：按服务的所在的盘符根路径进行创建，如果是linux系统，肯定会原样创建的。
     *  {
     *   "absolutePath": "D:\\tmp\\tomcat.2877894020006618597.7030\\work\\Tomcat\\localhost\\ROOT",
     *   "lastModifiedTime": "2023-10-28 10:08:13"
     * }
     * </pre>
     * <p>
     * <pre>
     * http://localhost:8080/fileServer/createDirByPath?dirPath=null/Temp/tomcat.8937263102917318832.8080/work/Tomcat/localhost/ROOT
     * windows系统上的效果。
     *  {
     *   "absolutePath": "D:\\project\\IDEA_project\\file-server\\target\\classes\\null\\Temp\\tomcat.8937263102917318832.8080\\work\\Tomcat\\localhost\\ROOT",
     *   "lastModifiedTime": "2023-10-28 10:06:16"
     * }
     * </pre>
     *
     * @param dirPath ：需要创建的目录(不是文件)，因为是get请求传参，所以路径必须使用左斜杠。防止报错：The valid characters are defined in RFC 7230 and RFC 3986
     * @return
     */
    @GetMapping("createDirByPath")
    @ResponseBody
    public Map<String, Object> createDirByPath(String dirPath) {
        Map<String, Object> dataMap = new HashMap<>(8);
        if (StrUtil.isNotBlank(dirPath)) {
            File mkdir = FileUtil.mkdir(dirPath);
            dataMap.put("absolutePath", mkdir.getAbsolutePath());
            dataMap.put("lastModifiedTime", DateUtil.date(mkdir.lastModified()).toString());
        }
        // 获取用户临时目录，如 C:\\Users\\Think\\AppData\\Local\\Temp\\
        String tmpDirPath = System.getProperty("java.io.tmpdir");
        dataMap.put("java.io.tmpdir", tmpDirPath);
        return dataMap;
    }

}
